home *** CD-ROM | disk | FTP | other *** search
- /* @(#)mod_servlet.c 1.7 97/06/16
- **
- ** Apache module for running Jeeves-compatible servlets.
- ** This Apache module lets you run Jeeves-style Servlets from your
- ** Apache web server.
- **
- ** To install the module:
- **
- ** 1) Get the *source* distribution of Apache, not a binary distribution,
- ** from "http://www.apache.org/dist/". Unpack it.
- **
- ** 2) Copy mod_servlet.c into the Apache src directory.
- **
- ** 3) Follow the instructions in the Apache src/INSTALL file:
- ** 3a) Edit Configuration, setting your system customizations as the
- ** comments direct, and adding this to the list of modules:
- ** Module servlet_module mod_servlet.o
- ** 3b) Configure
- ** 3c) make
- **
- ** 4) Continue with the instructions in the Apache README file:
- ** 4a) Copy the Apache conf/*.dist files to conf/*.conf.
- ** 4b) Edit conf/*.conf, setting your local system configuration.
- ** 4c) Add this to conf/srm.conf:
- ** <Location /servlet>
- ** SetHandler servlet-handler
- ** </Location>
- ** 4d) Add ServletConfig commands if you want to override the defaults:
- ** ServletConfig JAVA_HOME /opt/local/pkgs/java
- ** ServletConfig JAVA_EXE java
- ** ServletConfig JAVA_EXE_PATH /opt/local/pkgs/java/bin/java
- ** ServletConfig CLASSPATH /opt/local/pkgs/java/lib/classes.zip
- ** ServletConfig SERVAPI_HOME /opt/local/pkgs/ServAPI
- ** ServletConfig SERVLET_HOME /opt/local/pkgs/ServAPI/servlets
- ** ServletConfig SERVLET_CONFIG /opt/local/pkgs/ServAPI/servlets.properties
- ** ServletConfig NCGI_CLASS sun.servlet.apache.NcgiServletGate
- ** ServletConfig NCGI_HOST localhost
- ** ServletConfig NCGI_PORT 31461
- ** ServletConfig NCGI_AUTHFILE /tmp/ncgiauth
- ** 4e) Start apache. You should be able to run the module via
- ** a "/servlet" URL
- **
- ** The Apache home page is http://www.apache.org/
- ** The Apache module API is documented at http://www.apache.org/docs/API.html
- ** The Apache Module Registry: http://www.zyzzyva.com/server/module_registry/
- **
- ** The Jeeves home page is http://java.sun.com/products/jeeves/
- ** The Servlet API is documented at
- ** http://java.sun.com/products/jeeves/CurrentRelease/doc/api.html
- **
- **
- ** Copyright (c) 1996 Sun Microsystems, Inc. All Rights reserved
- ** Permission to use, copy, modify, and distribute this software
- ** and its documentation for NON-COMMERCIAL purposes and without
- ** fee is hereby granted provided that this copyright notice
- ** appears in all copies. Please refer to the file copyright.html
- ** for further important copyright and licensing information.
- **
- ** SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
- ** THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- ** TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- ** PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
- ** ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
- ** DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
- */
-
-
- /* Default values for config params. */
-
- #ifndef JAVA_HOME
- #define JAVA_HOME "/opt/local/pkgs/java"
- #endif
- #ifndef JAVA_EXE
- #define JAVA_EXE "java"
- #endif
- #ifndef CLASSPATH
- #define CLASSPATH "/opt/local/pkgs/java/lib/classes.zip"
- #endif
- #ifndef SERVAPI_HOME
- #define SERVAPI_HOME "/opt/local/pkgs/ServAPI"
- #endif
- #ifndef SERVLET_HOME
- #define SERVLET_HOME "/opt/local/pkgs/ServAPI/servlets"
- #endif
- #ifndef SERVLET_CONFIG
- #define SERVLET_CONFIG "/opt/local/pkgs/ServAPI/servlets.properties"
- #endif
- #ifndef NCGI_CLASS
- #define NCGI_CLASS "sun.servlet.apache.NcgiServletGate"
- #endif
- #ifndef NCGI_HOST
- #define NCGI_HOST "localhost"
- #endif
- #ifndef NCGI_PORT
- #define NCGI_PORT "31461"
- #endif
- #ifndef NCGI_AUTHFILE
- #define NCGI_AUTHFILE "/tmp/ncgiauth"
- #endif
- #ifndef NCGI_MAGIC
- #define NCGI_MAGIC "NCGI/1.0"
- #endif
-
-
- /* General includes. */
-
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <errno.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <netinet/in.h>
- #include <netdb.h>
-
-
- /* Apache includes. */
-
- #include "httpd.h"
- #include "http_config.h"
- #include "http_request.h"
- #include "http_core.h"
- #include "http_protocol.h"
- #include "http_main.h"
- #include "http_log.h"
- #include "util_script.h"
-
-
- /* NCGI client routines.
- **
- ** NCGI is a simple protocol for sending CGI requests over a network
- ** connection. It's the same basic idea as FastCGI (http://www.fastcgi.com/),
- ** but simpler. There are four parts:
- **
- ** The first part is to send a "magic" string identifying the protocol and
- ** version. That string is currently "NCGI/1.0". The string is preceeded
- ** by the length as an unsigned short in network order.
- **
- ** The second part is authorization. The server has previously written a
- ** random string to a designated file. The file is protected so that it
- ** can only be read by the userid running the CGI program. That program,
- ** the client, reads the file and sends the entire contents to the server,
- ** again preceeded by the length as an unsigned short in network order.
- **
- ** The third part is to send the request. Since CGI has already
- ** encapsulated the request as environment variables, the client just
- ** sends those strings. Again each one is preceeded by its length as an
- ** unsigned short in network order. A length of zero is sent at the end,
- ** indicating there are no more strings.
- **
- ** The fourth part is to send any data back and forth. Data is read from
- ** stdin and sent to the server, while simultaneously the response data
- ** from the server is read and then written to stdout. This continues
- ** until the client gets an EOF reading from the server. At that point
- ** the transaction is complete, the socket is closed, and the client
- ** exits.
- **
- ** The security of this protocol is pretty weak. The same authorization
- ** string is sent on every request, between server restarts - basically a
- ** password sent over the net in the clear. However, most of the time the
- ** network connection will only be to localhost, so that makes
- ** eavesdropping less of an issue. Still, future versions could improve
- ** the security, perhaps using nonces.
- */
-
- /* Find the host, make the socket, bind, and connect. */
- static int
- get_socket( char* host, int port, server_rec *s, int nolog )
- {
- struct hostent *he;
- struct sockaddr_in sin;
- int sock;
-
- he = gethostbyname( host );
- if ( he == (struct hostent*) 0 )
- {
- if ( ! nolog )
- log_error( "problem finding host", s );
- return -1;
- }
- bzero( (caddr_t) &sin, sizeof(sin) );
- sin.sin_family = AF_INET;
- sock = socket( AF_INET, SOCK_STREAM, 0 );
- if ( sock < 0 )
- {
- if ( ! nolog )
- log_unixerr( "socket", host, "problem creating socket", s );
- return -1;
- }
- if ( bind( sock, (struct sockaddr*) &sin, sizeof(sin) ) < 0 )
- {
- if ( ! nolog )
- log_unixerr( "bind", host, "problem binding to socket", s );
- return -1;
- }
- bcopy( he->h_addr, &sin.sin_addr, he->h_length );
- sin.sin_port = htons( port );
- if ( connect( sock, (struct sockaddr*) &sin, sizeof(sin) ) < 0 )
- {
- if ( ! nolog )
- log_unixerr( "connect", host, "problem connecting to socket", s );
- return -1;
- }
- return sock;
- }
-
-
- static int
- write_ushort( int fd, unsigned short us, server_rec* s )
- {
- unsigned short nus;
-
- nus = htons( us );
- if ( write( fd, &nus, sizeof(nus) ) != sizeof(nus) )
- {
- log_unixerr( "write", (char*) 0, "problem writing ushort", s );
- return -1;
- }
- return 0;
- }
-
-
- /* Send a string to the NCGI server. Each string is preceeded by its
- ** length as an unsigned short in network order.
- */
- static int
- write_string( int fd, char* cp, unsigned short len, server_rec* s )
- {
- if ( write_ushort( fd, len, s ) < 0 )
- return -1;
- if ( write( fd, cp, len ) != len )
- {
- log_unixerr( "write", (char*) 0, "problem writing string", s );
- return -1;
- }
- return 0;
- }
-
-
- /* Send the magic string to the NCGI server. */
- static int
- send_magic( int fd, server_rec* s )
- {
- if ( write_string( fd, NCGI_MAGIC, strlen( NCGI_MAGIC ), s ) < 0 )
- return -1;
- return 0;
- }
-
-
- /* Send the authorization string to the NCGI server. */
- static int
- send_auth( int fd, char* authfile, server_rec* s )
- {
- int afd;
- struct stat sb;
- size_t len;
- char* cp;
-
- afd = open( authfile, O_RDONLY );
- if ( afd < 0 )
- {
- log_unixerr( "open", authfile, "problem opening authfile", s );
- return -1;
- }
- if ( fstat( afd, &sb ) < 0 )
- {
- log_unixerr( "fstat", authfile, "problem statting authfile", s );
- (void) close( afd );
- return -1;
- }
- len = sb.st_size;
- cp = malloc( len );
- if ( cp == (char*) 0 )
- {
- log_error( "out of memory", s );
- (void) close( afd );
- return -1;
- }
- if ( read( afd, cp, len ) != len )
- {
- log_unixerr( "read", authfile, "problem reading authfile", s );
- (void) close( afd );
- free( cp );
- return -1;
- }
- if ( write_string( fd, cp, len, s ) < 0 )
- {
- (void) close( afd );
- free( cp );
- return -1;
- }
- (void) close( afd );
- free( cp );
- return 0;
- }
-
-
- /* Send an environment variable to the NCGI server. var should
- ** be passed in as "name=value" form.
- */
- static int
- send_var( int fd, char* var, server_rec* s )
- {
- unsigned short len;
-
- len = strlen( var );
- if ( len != 0 )
- if ( write_string( fd, var, len, s ) < 0 )
- return -1;
- return 0;
- }
-
-
- /* Tell the NCGI server that we're done sending environment variables. */
- static int
- send_done_vars( int fd, server_rec* s )
- {
- unsigned short len;
-
- len = 0;
- if ( write_ushort( fd, len, s ) < 0 )
- return -1;
- return 0;
- }
-
-
- /* Apache definitions. */
-
- typedef struct
- {
- char* java_home;
- char* java_exe;
- char* java_exe_path;
- char* classpath;
- char* servapi_home;
- char* servlet_home;
- char* servlet_config;
- char* ncgi_class;
- char* ncgi_host;
- char* ncgi_port;
- char* ncgi_authfile;
- } servlet_conf;
-
-
- /* Apache routines. */
-
-
- /* Forward declaration for module object. */
- module servlet_module;
-
-
- /* Make a config record. */
- static void*
- make_servlet_conf( pool *p, server_rec *s )
- {
- servlet_conf* c = (servlet_conf*) pcalloc( p, sizeof(servlet_conf) );
-
- c->java_home = JAVA_HOME;
- c->java_exe = JAVA_EXE;
- c->java_exe_path = (char*) 0;
- c->classpath = CLASSPATH;
- c->servapi_home = SERVAPI_HOME;
- c->servlet_home = SERVLET_HOME;
- c->servlet_config = SERVLET_CONFIG;
- c->ncgi_class = NCGI_CLASS;
- c->ncgi_host = NCGI_HOST;
- c->ncgi_port = NCGI_PORT;
- c->ncgi_authfile = NCGI_AUTHFILE;
-
- return (void*) c;
- }
-
-
- /* Java child process. Execs the java runtime and starts up the
- ** NCGI server / servlet runner.
- */
- static void
- java_child( void* data )
- {
- server_rec* s = (server_rec*) data;
- servlet_conf* c = get_module_config( s->module_config, &servlet_module );
- char executable[HUGE_STRING_LEN];
- char* oclasspath;
- char classpath[HUGE_STRING_LEN];
- char path[HUGE_STRING_LEN];
- char tz[HUGE_STRING_LEN];
- char* env[4];
- int n;
-
- if ( c->java_exe_path != (char*) 0 )
- (void) strcpy( executable, c->java_exe_path );
- else
- (void) sprintf( executable, "%s/bin/%s", c->java_home, c->java_exe );
-
- n = 0;
- if (c->classpath != (char*) 0)
- oclasspath = c->classpath;
- else
- oclasspath = getenv( "CLASSPATH" );
-
- if ( oclasspath == (char*) 0 )
- (void) sprintf(
- classpath, "CLASSPATH=%s/classes:%s/lib/classes.zip:%s",
- c->servapi_home, c->servapi_home , c->servlet_home);
- else
- (void) sprintf(
- classpath, "CLASSPATH=%s/classes:%s/lib/classes.zip:%s:%s",
- c->servapi_home, c->servapi_home, c->servlet_home, oclasspath );
- env[n++] = classpath;
- if ( getenv( "PATH" ) != (char*) 0 )
- {
- (void) sprintf( path, "PATH=%s", getenv( "PATH" ) );
- env[n++] = path;
- }
- if ( getenv( "TZ" ) != (char*) 0 )
- {
- (void) sprintf( tz, "TZ=%s", getenv( "TZ" ) );
- env[n++] = tz;
- }
- env[n++] = (char*) 0;
-
- error_log2stderr ( s );
- cleanup_for_exec();
- execle(
- executable, c->java_exe, c->ncgi_class,
- "-p", c->ncgi_port, "-a", c->ncgi_authfile,
- "-s", c->servlet_config,
- (char*) 0, env );
- perror( "exec java" );
- exit( 1 );
- }
-
-
- /* Make sure a filename exists and is read-protected. */
- static void
- create_and_protect( char* filename, server_rec* s )
- {
- int fd;
-
- fd = open( filename, O_RDWR );
- if ( fd < 0 )
- {
- fd = open( filename, O_RDWR|O_CREAT );
- if ( fd < 0 )
- {
- log_unixerr( "open", filename, "problem creating file", s );
- return;
- }
- }
- (void) fchmod( fd, 0600 );
- (void) close( fd );
- }
-
-
- /* Initialization routine. Spawns child process to run the server. */
- static void
- servlet_init_nonroot( server_rec* s, pool* p )
- {
- servlet_conf* c = get_module_config( s->module_config, &servlet_module );
- int sock;
- int i;
-
- /* Try connecting. */
- sock = get_socket( c->ncgi_host, atoi( c->ncgi_port ), s, 1 );
- if ( sock >= 0 )
- {
- /* Connect worked, the server must already be running. */
- (void) close( sock );
- return;
- }
-
- /* Connect failed. Start the server. */
- create_and_protect( c->ncgi_authfile, s );
- if ( spawn_child(
- p, java_child, (void*) s, kill_never,
- (FILE**) 0, (FILE**) 0 ) == 0 )
- {
- log_error( "couldn't spawn java process", s );
- return;
- }
-
- /* Make a few tries to connect. */
- for ( i = 0; i < 5; ++i )
- {
- sleep( 2 );
- sock = get_socket( c->ncgi_host, atoi( c->ncgi_port ), s, 1 );
- if ( sock >= 0 )
- {
- (void) close( sock );
- return;
- }
- }
- /* If we couldn't connect, just give up. */
- }
-
-
- /* ServletConfig command. */
- static char*
- servlet_config( cmd_parms* parms, void* dummy, char* var, char* val )
- {
- server_rec* s = parms->server;
- servlet_conf* c = get_module_config( s->module_config, &servlet_module );
- if ( strcasecmp( var, "JAVA_HOME" ) == 0 )
- c->java_home = val;
- else if ( strcasecmp( var, "JAVA_EXE" ) == 0 )
- c->java_exe = val;
- else if ( strcasecmp( var, "JAVA_EXE_PATH" ) == 0 )
- c->java_exe_path = val;
- else if ( strcasecmp( var, "CLASSPATH" ) == 0 )
- c->classpath = val;
- else if ( strcasecmp( var, "SERVAPI_HOME" ) == 0 )
- c->servapi_home = val;
- else if ( strcasecmp( var, "SERVLET_HOME" ) == 0 )
- c->servlet_home = val;
- else if ( strcasecmp( var, "SERVLET_CONFIG" ) == 0 )
- c->servlet_config = val;
- else if ( strcasecmp( var, "NCGI_CLASS" ) == 0 )
- c->ncgi_class = val;
- else if ( strcasecmp( var, "NCGI_HOST" ) == 0 )
- c->ncgi_host = val;
- else if ( strcasecmp( var, "NCGI_PORT" ) == 0 )
- c->ncgi_port = val;
- else if ( strcasecmp( var, "NCGI_AUTHFILE" ) == 0 )
- c->ncgi_authfile = val;
- else
- return "unknown variable in ServletConfig command";
- return (char*) 0;
- }
-
-
- /* Cleanup routine for request handler. */
- static void
- cleanup_handler( request_rec* r, int sock, void (*handler)() )
- {
- if ( sock != -1 )
- (void) close( sock );
- if ( handler != 0 )
- signal( SIGPIPE, handler );
- kill_timeout( r );
- }
-
-
- /* Request handler. Called on each request. */
- static int
- servlet_handler( request_rec* r )
- {
- static int inited = 0;
- server_rec* s = r->server;
- table* e = r->subprocess_env;
- request_rec* pa_req;
- servlet_conf* c = get_module_config( s->module_config, &servlet_module );
- char* lenp = table_get( r->headers_in, "Content-length" );
- void (*handler)() = 0;
- int sock = -1;
- char** env;
- char** ep;
- char databuffer[HUGE_STRING_LEN];
- int path_info_start;
- char* path_info_str;
-
- /* Initialize if necessary. We call the init routine from the request
- ** handler rather than via the init entry in the module struct because
- ** if we did it the latter way, it would get called as root!
- */
- if ( ! inited )
- {
- inited = 1;
- servlet_init_nonroot( s, permanent_pool );
- }
-
- /* Check for bad request. */
- if ( ( r->method_number == M_POST || r->method_number == M_PUT ) &&
- lenp == (char*) 0 )
- {
- log_reason( "POST or PUT without Content-length:", r->uri, r );
- return BAD_REQUEST;
- }
-
- /* Set up timeout. */
- hard_timeout( "run servlet", r );
- handler = signal( SIGPIPE, SIG_IGN );
-
- /* Find the host, make the socket, bind, and connect. */
- sock = get_socket( c->ncgi_host, atoi( c->ncgi_port ), s, 0 );
- if ( sock < 0 )
- {
- log_reason( "problem creating socket", r->uri, r );
- cleanup_handler( r, sock, handler );
- return SERVER_ERROR;
- }
-
- /* Send the magic string. */
- if ( send_magic( sock, s ) < 0 )
- {
- log_reason( "problem sending magic", r->uri, r );
- cleanup_handler( r, sock, handler );
- return SERVER_ERROR;
- }
-
- /* Send the authorization file. */
- if ( send_auth( sock, c->ncgi_authfile, s ) < 0 )
- {
- log_reason( "problem sending auth string", r->uri, r );
- cleanup_handler( r, sock, handler );
- return SERVER_ERROR;
- }
-
- /* Make a CGI environment array. */
- add_common_vars( r );
-
- /* add_cgi_vars( r );
- *
- * essentially have to rewrite this method below
- */
-
- /* Adjusting variables for servlet location
- */
-
- table_set (e, "GATEWAY_INTERFACE","servlet");
- table_set (e, "SERVER_PROTOCOL", r->protocol);
- table_set (e, "REQUEST_METHOD", r->method);
- table_set (e, "QUERY_STRING", r->args ? r->args : "");
-
- if (strncmp(r->uri,"/servlet/",strlen("/servlet/"))) {
- log_reason( "servlets must be in /servlet directory", r->uri, r );
- cleanup_handler( r, sock, handler );
- return SERVER_ERROR;
- }
- if (!r->path_info || !r->path_info[0]) {
- /* someone is trying to get to the "servlets directory". tsk.*/
- cleanup_handler( r, sock, handler );
- return FORBIDDEN;
- }
-
- /* length = total - path_info + strlen("/servlet/") */
-
- path_info_str = strchr(r->uri + strlen("/servlet/"),'/');
- if (path_info_str) {
- r->path_info = path_info_str;
- path_info_start = strlen(r->uri) - strlen(r->path_info);
- r->uri[path_info_start] = '\0';
- table_set (e, "SCRIPT_NAME", r->uri);
- r->uri[path_info_start] = '/';
-
- pa_req = sub_req_lookup_uri(
- escape_uri(r->pool, r->path_info), r);
- table_set(e, "PATH_INFO", r->path_info);
- if (pa_req->filename) {
- table_set (e, "PATH_TRANSLATED",
- pstrcat (r->pool, pa_req->filename, pa_req->path_info, NULL));
- }
- destroy_sub_req(pa_req);
- } else {
- table_set (e, "SCRIPT_NAME", r->uri);
- }
-
- env = create_environment( r->pool, r->subprocess_env );
-
- /* Send all environment variables to NCGI server. Each string is
- ** preceeded by its length as an unsigned short in network order.
- ** After the last string, a length of zero is sent.
- */
- for ( ep = env; *ep != (char*) 0; ++ep )
- if ( send_var( sock, *ep, s ) < 0 )
- {
- log_reason( "problem sending environment string", r->uri, r );
- cleanup_handler( r, sock, handler );
- return SERVER_ERROR;
- }
- if ( send_done_vars( sock, s ) < 0 )
- {
- log_reason( "problem sending env end marker", r->uri, r );
- cleanup_handler( r, sock, handler );
- return SERVER_ERROR;
- }
-
- /* Transfer any put/post data, CERN style...
- ** Note that if a buggy servlet fails to read everything we throw
- ** at it, or a buggy client sends too much, we get a SIGPIPE, so
- ** we have to ignore SIGPIPE while doing this. CERN does the same
- ** (and in fact, they pretty nearly guarantee themselves a SIGPIPE
- ** on every invocation by chasing the real client data with a
- ** spurious newline).
- */
- if ( r->method_number == M_POST || r->method_number == M_PUT )
- {
- int remaining = atoi( lenp );
- int ok_to_write = 1;
-
- while ( remaining > 0 )
- {
- int len_read, len_to_read = remaining;
- if ( len_to_read > sizeof(databuffer) )
- len_to_read = sizeof(databuffer);
- len_read = read_client_block( r, databuffer, len_to_read );
- if ( len_read == 0 )
- break;
- if ( ok_to_write )
- if ( write( sock, databuffer, len_read ) < 0 )
- ok_to_write = 0;
- remaining -= len_read;
- }
- }
-
- /* Send back the results. */
- bflush( r->connection->client );
- for (;;)
- {
- int len_read = read( sock, databuffer, sizeof(databuffer) );
- if ( len_read <= 0 )
- break;
- if ( write( r->connection->client->fd, databuffer, len_read ) <= 0 )
- break;
- }
-
- /* All done. */
- cleanup_handler( r, sock, handler );
- return OK; /* NOT r->status, even if it has changed */
- }
-
-
- /* Command table. */
- static command_rec servlet_commands [] =
- {
- { "ServletConfig", servlet_config, NULL, ACCESS_CONF|RSRC_CONF, TAKE2,
- "a variable to set followed by its value" },
- NULL
- };
-
- /* Handler record. */
- static handler_rec servlet_handlers[] =
- {
- { "servlet-handler", servlet_handler },
- NULL
- };
-
- /* Module structure. */
- module servlet_module =
- {
- STANDARD_MODULE_STUFF,
- NULL, /* initializer */
- NULL, /* dir config creater */
- NULL, /* dir merger - default is to override */
- make_servlet_conf, /* server config */
- NULL, /* merge server config */
- servlet_commands, /* command table */
- servlet_handlers, /* handlers */
- NULL, /* filename translation */
- NULL, /* check_user_id */
- NULL, /* check auth */
- NULL, /* check access */
- NULL, /* type_checker */
- NULL, /* fixups */
- NULL /* logger */
- };
-